#pragma once

#include <mcp/config.h>
#include <mcp/forward.h>
#include <mcp/types.h>
#include <mcp/error.h>

#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <optional>
#include <functional>

namespace mcp {
namespace auth {

/// @brief Authentication credentials
struct Credentials {
    std::string username;
    std::string password;
    std::optional<std::string> api_key;
    std::optional<std::string> bearer_token;
    std::optional<std::string> client_id;
    std::optional<std::string> client_secret;
    std::unordered_map<std::string, std::string> additional_data;

    Credentials() = default;
    Credentials(std::string username, std::string password)
        : username(std::move(username)), password(std::move(password)) {}
};

/// @brief Authentication result
struct AuthResult {
    bool success = false;
    std::string access_token;
    std::string refresh_token;
    int expires_in = 0;
    std::string token_type = "Bearer";
    std::unordered_map<std::string, std::string> additional_data;
};

/// @brief Authentication challenge
struct AuthChallenge {
    std::string scheme;
    std::string realm;
    std::unordered_map<std::string, std::string> parameters;
};

/// @brief Base authenticator interface
class Authenticator {
public:
    virtual ~Authenticator() = default;

    /// @brief Authenticate with credentials
    /// @param credentials Authentication credentials
    /// @return Authentication result or error
    virtual Result<AuthResult> authenticate(const Credentials& credentials) = 0;

    /// @brief Refresh access token
    /// @param refresh_token Refresh token
    /// @return New authentication result or error
    virtual Result<AuthResult> refresh(const std::string& refresh_token) = 0;

    /// @brief Validate access token
    /// @param access_token Access token to validate
    /// @return true if valid, false otherwise
    virtual bool validate_token(const std::string& access_token) = 0;

    /// @brief Get authentication scheme
    /// @return Authentication scheme name
    virtual std::string get_scheme() const = 0;

    /// @brief Get supported challenges
    /// @return List of supported authentication challenges
    virtual std::vector<AuthChallenge> get_challenges() const = 0;

    /// @brief Check if authenticator supports token refresh
    /// @return true if refresh is supported, false otherwise
    virtual bool supports_refresh() const = 0;
};

/// @brief Basic authentication authenticator
class BasicAuthenticator : public Authenticator {
public:
    explicit BasicAuthenticator(std::string realm = "MCP Server");
    ~BasicAuthenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

private:
    std::string realm_;
};

/// @brief Bearer token authenticator
class BearerAuthenticator : public Authenticator {
public:
    explicit BearerAuthenticator(std::string realm = "MCP Server");
    ~BearerAuthenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

private:
    std::string realm_;
};

/// @brief API key authenticator
class ApiKeyAuthenticator : public Authenticator {
public:
    explicit ApiKeyAuthenticator(std::string header_name = "X-API-Key",
                                std::string realm = "MCP Server");
    ~ApiKeyAuthenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

private:
    std::string header_name_;
    std::string realm_;
};

/// @brief Authenticator factory
class AuthenticatorFactory {
public:
    static AuthenticatorFactory& instance();

    /// @brief Register an authenticator type
    /// @param scheme Authentication scheme
    /// @param factory Function to create authenticator
    void register_authenticator(const std::string& scheme,
                                std::function<std::unique_ptr<Authenticator>()> factory);

    /// @brief Create authenticator by scheme
    /// @param scheme Authentication scheme
    /// @return Authenticator instance or nullptr
    std::unique_ptr<Authenticator> create_authenticator(const std::string& scheme);

    /// @brief Get registered schemes
    /// @return List of registered authentication schemes
    std::vector<std::string> get_registered_schemes() const;

    /// @brief Check if scheme is supported
    /// @param scheme Authentication scheme to check
    /// @return true if supported, false otherwise
    bool is_supported(const std::string& scheme) const;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
    AuthenticatorFactory();
};

} // namespace auth
} // namespace mcp